home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Library / Manuels & Misc / Assembly / AOA.ZIP / CH16 / DATEPAT.ASM < prev    next >
Encoding:
Assembly Source File  |  1994-07-11  |  9.8 KB  |  442 lines

  1. ; datepat.asm
  2. ;
  3. ; This program converts dates of various formats to a three integer
  4. ; component value- month, day, and year.
  5.  
  6.         .xlist
  7.         .286
  8.         include     stdlib.a
  9.         includelib    stdlib.lib
  10.         matchfuncs
  11.         .list
  12.         .lall
  13.  
  14.  
  15. dseg        segment    para public 'data'
  16.  
  17. ; The following three variables hold the result of the conversion.
  18.  
  19. month        word    0
  20. day        word    0
  21. year        word    0
  22.  
  23. ; StrPtr is a double word value that points at the string under test.
  24. ; The output routines use this variable.  It is declared as two word
  25. ; values so it is easier to store es:di into it.
  26.  
  27. strptr        word    0,0
  28.  
  29. ; Value is a generic variable the ConvertInt routine uses
  30.  
  31. value        word    0
  32.  
  33.  
  34.  
  35. ; Number of valid days in each month (Feb is handled specially)
  36.  
  37. DaysInMonth    byte    31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  38.  
  39.  
  40.  
  41. ; Some sample strings to test the date conversion routines.
  42.  
  43. Str0        byte    "Feb 4, 1956",0
  44. Str1        byte    "July 20, 1960",0
  45. Str2        byte    "Jul 8, 1964",0
  46. Str3        byte    "1/1/97",0
  47. Str4        byte    "1-1-1997",0
  48. Str5        byte    "12-25-74",0
  49. Str6        byte    "3/28/1981",0
  50. Str7        byte    "January 1, 1999",0
  51. Str8        byte    "Feb 29, 1996",0
  52. Str9        byte    "30 June, 1990",0
  53. Str10        byte    "August 7, 1945",0
  54. Str11        byte    "30 September, 1992",0
  55. Str12        byte    "Feb 29, 1990",0
  56. Str13        byte    "29 Feb, 1992",0
  57.  
  58.  
  59.  
  60. ; The following grammar is what we use to process the dates
  61. ;
  62. ; Date    ->    EngMon Integer Integer
  63. ;    |    Integer EngMon Integer
  64. ;    |    Integer "/" Integer "/" Integer
  65. ;    |    Integer "-" Integer "-" Integer
  66. ;
  67. ; EngMon->    Jan | January | Feb | February | ... | Dec | December
  68. ; Integer->    digit integer | digit
  69. ; digit    ->    0 | 1 | ... | 9
  70. ;
  71. ; Some semantic rules this code has to check:
  72. ;
  73. ; If the year is in the range 0-99, this code has to add 1900 to it.
  74. ; If the year is not in the range 0-99 or 1900-1999 then return an error.
  75. ; The month must be in the range 1-12, else return an error.
  76. ; The day must be between one and 28, 29, 30, or 31.  The exact maximum
  77. ; day depends on the month.
  78.  
  79.  
  80. separators    pattern    {spancset, delimiters}
  81.  
  82.  
  83. ; DatePat processes dates of the form  "MonInEnglish  Day  Year"
  84.  
  85. DatePat        pattern    {sl_match2, EngMon, DatePat2, DayYear}
  86. DayYear        pattern    {sl_match2, DayInteger, 0, YearPat}
  87. YearPat        pattern    {sl_match2, YearInteger}
  88.  
  89. ; DatePat2 processes dates of the form "Day MonInEng Year"
  90.  
  91. DatePat2    pattern    {sl_match2, DayInteger, DatePat3, MonthYear}
  92. MonthYear    pattern    {sl_match2, EngMon, 0, YearPat}
  93.  
  94. ; DatePat3 processes dates of the form "mm-dd-yy"
  95.  
  96. DatePat3    pattern    {sl_match2, MonInteger, DatePat4, DatePat3a}
  97. DatePat3a    pattern    {sl_match2, separators, DatePat3b, DatePat3b}
  98. DatePat3b    pattern    {matchchar, '-', 0, DatePat3c}
  99. DatePat3c    pattern    {sl_match2, DayInteger, 0, DatePat3d}
  100. DatePat3d    pattern    {sl_match2, separators, DatePat3e, DatePat3e}
  101. DatePat3e    pattern    {matchchar, '-', 0, DatePat3f}
  102. DatePat3f    pattern    {sl_match2, YearInteger}
  103.  
  104. ; DatePat4 processes dates of the form "mm/dd/yy"
  105.  
  106. DatePat4    pattern    {sl_match2, MonInteger, 0, DatePat4a}
  107. DatePat4a    pattern    {sl_match2, separators, DatePat4b, DatePat4b}
  108. DatePat4b    pattern    {matchchar, '/', 0, DatePat4c}
  109. DatePat4c    pattern    {sl_match2, DayInteger, 0, DatePat4d}
  110. DatePat4d    pattern    {sl_match2, separators, DatePat4e, DatePat4e}
  111. DatePat4e    pattern    {matchchar, '/', 0, DatePat4f}
  112. DatePat4f    pattern    {sl_match2, YearInteger}
  113.  
  114.  
  115. ; DayInteger matches an decimal string, converts it to an integer, and
  116. ; stores the result away in the Day variable.
  117.  
  118. DayInteger    pattern    {sl_match2, Integer, 0, SetDayPat}
  119. SetDayPat    pattern    {SetDay}
  120.  
  121. ; MonInteger matches an decimal string, converts it to an integer, and
  122. ; stores the result away in the Month variable.
  123.  
  124. MonInteger    pattern    {sl_match2, Integer, 0, SetMonPat}
  125. SetMonPat    pattern    {SetMon}
  126.  
  127. ; YearInteger matches an decimal string, converts it to an integer, and
  128. ; stores the result away in the Year variable.
  129.  
  130.  
  131. YearInteger    pattern    {sl_match2, Integer, 0, SetYearPat}
  132. SetYearPat    pattern    {SetYear}
  133.  
  134.  
  135. ; Integer skips any leading delimiter characters and then matches a
  136. ; decimal string.  The Integer0 pattern matches exactly the decimal
  137. ; characters; the code does a patgrab on Integer0 when converting
  138. ; this string to an integer.
  139.  
  140. Integer        pattern    {sl_match2, separators, 0, Integer0}
  141. Integer0    pattern    {sl_match2, number, 0, Convert2Int}
  142. number        pattern    {anycset, digits, 0, number2}
  143. number2        pattern    {spancset, digits}
  144. Convert2Int    pattern    {ConvertInt}
  145.  
  146.  
  147.  
  148.  
  149. ; A macro to make it easy to declare each of the 24 English month
  150. ; patterns (24 because we allow the full month name and an
  151. ; abbreviation).
  152.  
  153. MoPat        macro    name, next, str, str2, value
  154.         local   SetMo, string, full, short, string2, doMon
  155.  
  156. name        pattern    {sl_match2, short, next}
  157. short        pattern    {matchistr, string2, full, SetMo}
  158. full        pattern    {matchistr, string, 0, SetMo}
  159.  
  160. string        byte    str
  161.         byte    0
  162.  
  163. string2        byte    str2
  164.         byte    0
  165.  
  166. SetMo        pattern    {MonthVal, value}
  167.         endm
  168.  
  169.  
  170. ; EngMon is a chain of patterns that match one of the strings
  171. ; JAN, JANUARY, FEB, FEBRUARY, etc.  The last parameter to the
  172. ; MoPat macro is the month number.
  173.  
  174. EngMon        pattern {sl_match2, separators, jan, jan}
  175.         MoPat    jan, feb, "JAN", "JANUARY", 1
  176.         MoPat    feb, mar, "FEB", "FEBRUARY", 2
  177.         MoPat    mar, apr, "MAR", "MARCH", 3
  178.         MoPat    apr, may, "APR", "APRIL", 4
  179.         MoPat    may, jun, "MAY", "MAY", 5
  180.         MoPat    jun, jul, "JUN", "JUNE", 6
  181.         MoPat    jul, aug, "JUL", "JULY", 7
  182.         MoPat    aug, sep, "AUG", "AUGUST", 8
  183.         MoPat    sep, oct, "SEP", "SEPTEMBER", 9
  184.         MoPat    oct, nov, "OCT", "OCTOBER", 10
  185.         MoPat    nov, decem, "NOV", "NOVEMBER", 11
  186.         MoPat    decem, 0, "DEC", "DECEMBER", 12
  187.  
  188.  
  189.  
  190.  
  191. ; We use the "digits" and "delimiters" sets from the standard library.
  192.  
  193.         include    stdsets.a
  194.  
  195. dseg        ends
  196.  
  197.  
  198.  
  199. cseg        segment    para public 'code'
  200.         assume    cs:cseg, ds:dseg
  201.  
  202.  
  203. ; ConvertInt-    Matches a sequence of digits and converts them to an integer.
  204.  
  205. ConvertInt    proc    far
  206.         push    ds
  207.         push    es
  208.         push    di
  209.         mov    ax, dseg
  210.         mov    ds, ax
  211.  
  212.         lesi    Integer0    ;Integer0 contains the decimal
  213.         patgrab            ; string we matched, grab that
  214.         atou            ; string and convert it to an
  215.         mov    Value, ax    ; integer and save the result.
  216.         free            ;Free mem allocated by patgrab.
  217.  
  218.         pop    di
  219.         mov    ax, di        ;Required by sl_match.
  220.         pop    es
  221.         pop    ds
  222.         stc            ;Always succeed.
  223.         ret
  224.  
  225. ConvertInt    endp
  226.  
  227.  
  228. ; SetDay, SetMon, and SetYear simply copy value to the appropriate
  229. ; variable.
  230.  
  231. SetDay        proc    far
  232.         push    ds
  233.         mov    ax, dseg
  234.         mov    ds, ax
  235.         mov    ax, value
  236.         mov    day, ax
  237.         mov    ax, di
  238.         pop    ds
  239.         stc
  240.         ret
  241. SetDay        endp
  242.  
  243.  
  244. SetMon        proc    far
  245.         push    ds
  246.         mov    ax, dseg
  247.         mov    ds, ax
  248.         mov    ax, value
  249.         mov    Month, ax
  250.         mov    ax, di
  251.         pop    ds
  252.         stc
  253.         ret
  254. SetMon        endp
  255.  
  256.  
  257. SetYear        proc    far
  258.         push    ds
  259.         mov    ax, dseg
  260.         mov    ds, ax
  261.         mov    ax, value
  262.         mov    Year, ax
  263.         mov    ax, di
  264.         pop    ds
  265.         stc
  266.         ret
  267. SetYear        endp
  268.  
  269.  
  270. ; MonthVal is a pattern used by the English month patterns.
  271. ; This pattern function simply copies the matchparm field to
  272. ; the month variable (the matchparm field is passed in si).
  273.  
  274. MonthVal    proc    far
  275.         push    ds
  276.         mov    ax, dseg
  277.         mov    ds, ax
  278.         mov    Month, si
  279.         mov    ax, di
  280.         pop    ds
  281.         stc
  282.         ret
  283. MonthVal    endp
  284.  
  285.  
  286.  
  287. ; ChkDate-    Checks a date to see if it is valid.  Returns with the
  288. ;        carry flag set if it is, clear if not.
  289.  
  290. ChkDate        proc    far
  291.         push    ds
  292.         push    ax
  293.         push    bx
  294.  
  295.         mov    ax, dseg
  296.         mov    ds, ax
  297.  
  298. ; If the year is in the range 0-99, add 1900 to it.
  299. ; Then check to see if it's in the range 1900-1999.
  300.  
  301.         cmp    Year, 100
  302.         ja    Notb100
  303.         add    Year, 1900
  304. Notb100:    cmp    Year, 2000
  305.         jae    BadDate
  306.         cmp    Year, 1900
  307.         jb    BadDate
  308.  
  309. ; Okay, make sure the month is in the range 1-12
  310.  
  311.         cmp    Month, 12
  312.         ja    BadDate
  313.         cmp    Month, 1
  314.         jb    BadDate
  315.  
  316. ; See if the number of days is correct for all months except Feb:
  317.  
  318.         mov    bx, Month
  319.         mov    ax, Day            ;Make sure Day <> 0.
  320.         test    ax, ax
  321.         je    BadDate
  322.         cmp    ah, 0            ;Make sure Day < 256.
  323.         jne    BadDate
  324.  
  325.         cmp    bx, 2            ;Handle Feb elsewhere.
  326.         je    DoFeb
  327.         cmp    al, DaysInMonth[bx-1]    ;Check against max val.
  328.         ja    BadDate
  329.         jmp    GoodDate
  330.  
  331. ; Kludge to handle leap years.  Note that 1900 is *not* a leap year.
  332.  
  333. DoFeb:          cmp    ax, 29            ;Only applies if day is
  334.         jb    GoodDate        ; equal to 29.
  335.         ja    BadDate            ;Error if Day > 29.
  336.         mov    bx, Year        ;1900 is not a leap year
  337.         cmp    bx, 1900        ; so handle that here.
  338.         je    BadDate
  339.         and    bx, 11b            ;Else, Year mod 4 is a
  340.         jne    BadDate            ; leap year.
  341.  
  342. GoodDate:    pop    bx
  343.         pop    ax
  344.         pop    ds
  345.         stc
  346.         ret
  347.  
  348. BadDate:    pop    bx
  349.         pop    ax
  350.         pop    ds
  351.         clc
  352.         ret
  353. ChkDate        endp
  354.  
  355.  
  356. ; ConvertDate-    ES:DI contains a pointer to a string containing a valid
  357. ;        date.  This routine converts that date to the three
  358. ;        integer values found in the Month, Day, and Year
  359. ;        variables.  Then it prints them to verify the pattern
  360. ;        matching routine.
  361.  
  362. ConvertDate    proc    near
  363.  
  364.         ldxi    DatePat
  365.         xor    cx, cx
  366.         match
  367.         jnc    NoMatch
  368.  
  369.         mov    strptr, di        ;Save string pointer for
  370.         mov    strptr+2, es        ; use by printf
  371.  
  372.         call    ChkDate            ;Validate the date.
  373.         jnc    NoMatch
  374.  
  375.         printf
  376.         byte    "%-20^s = Month: %2d    Day:   %2d   Year:  %4d\n",0
  377.         dword    strptr, Month, Day, Year
  378.         jmp    Done
  379.  
  380. NoMatch:    printf
  381.         byte    "Illegal date ('%^s')",cr,lf,0
  382.         dword    strptr
  383.  
  384. Done:        ret
  385. ConvertDate    endp
  386.  
  387.  
  388.  
  389.  
  390. Main        proc
  391.         mov    ax, dseg
  392.         mov    ds, ax
  393.         mov    es, ax
  394.  
  395.         meminit                ;Init memory manager.
  396.  
  397. ; Call ConvertDate to test several different date strings.
  398.  
  399.         lesi    Str0
  400.         call    ConvertDate
  401.         lesi    Str1
  402.         call    ConvertDate
  403.         lesi    Str2
  404.         call    ConvertDate
  405.         lesi    Str3
  406.         call    ConvertDate
  407.         lesi    Str4
  408.         call    ConvertDate
  409.         lesi    Str5
  410.         call    ConvertDate
  411.         lesi    Str6
  412.         call    ConvertDate
  413.         lesi    Str7
  414.         call    ConvertDate
  415.         lesi    Str8
  416.         call    ConvertDate
  417.         lesi    Str9
  418.         call    ConvertDate
  419.         lesi    Str10
  420.         call    ConvertDate
  421.         lesi    Str11
  422.         call    ConvertDate
  423.         lesi    Str12
  424.         call    ConvertDate
  425.         lesi    Str13
  426.         call    ConvertDate
  427.  
  428.  
  429. Quit:        ExitPgm
  430. Main        endp
  431.  
  432. cseg        ends
  433.  
  434. sseg        segment    para stack 'stack'
  435. stk        db    1024 dup ("stack   ")
  436. sseg        ends
  437.  
  438. zzzzzzseg    segment    para public 'zzzzzz'
  439. LastBytes    db    16 dup (?)
  440. zzzzzzseg    ends
  441.         end    Main
  442.